/*
    服务注册中心服务端模块（业务层）：服务注册中心服务端功能的实现
        1、服务注册中心管理器（PDManager）：对服务提供者和发现者维护和管理，提供服务注册、发现、上线、下线通知
            1.1、提供一个服务请求回调，注册给Dispatcher调度器，当接收到服务注册请求 & 服务发现请求时，会调用此函数进行处理
            1.2、提供一个连接断开回调，当连接断开的时候，判断是服务提供者还是发现者
                 如果是提供者，则进行下线通知，同时删除注册中心的所有信息
                 如果是发现者，直接删除注册中心的所有信息
            1.3、其中对服务提供者和发现者的维护和管理进行抽离，分别封装实现为：ProviderManager、DiscovererManager
        2、提供者管理类（ProviderManager）：对所有服务提供者维护和管理
            2.1、提供一个映射表（映射关系<连接，提供者>），将连接和提供者保存起来，方便感知提供者什么时候提供服务/不再提供服务
            2.2、提供一个映射表（映射关系<服务名，提供者集合>），将服务名和提供该服务的所有提供者保存起来，方便发现者发现服务
            2.3、提供添加服务提供者接口、删除服务提供者接口、获取服务提供者接口以及服务发现时获取指定服务的所有提供者信息接口
            2.4、对提供者进行抽象描述成Provider
        3、提供者类（Provider）：对服务提供者的描述
            3.1、提供当前提供者的长连接、地址（ip & port）、提供提供的所有服务名集合属性
            3.2、提供添加服务名接口
        4、发现者管理类（DiscovererManager）：对有所发现者维护和管理
            4.1、提供一个映射表（映射关系<连接，发现者>），将连接和发现者保存起来，方便感知发现者什么时候发现服务/不再发现服务
            4.2、提供一个映射表（映射关系<服务名，发现者集合>），将服务名和发现该服务的所有发现者保存起来，方便服务的上下线通知
            4.3、对发现者进行抽象描述成Discoverer
        5、发现者类（Discoverer）：对服务发现者的描述
            5.1、提供当前发现者的长连接、发现者发现的所有服务名集合属性
            5.2、提供添加服务名接口
*/
#pragma once
#include "../common/message.hpp"
#include "../common/net.hpp"
#include "../common/uuid.hpp"
#include <set>

namespace ns_jsonrpc
{
    namespace ns_server
    {
        // 服务提供者管理类
        class ProviderManager
        {
        public:
            using ProviderManagerPtr = std::shared_ptr<ProviderManager>;

        public:
            // 提供者
            struct Provider
            {
                using ProviderPtr = std::shared_ptr<Provider>;
                BaseConnection::BaseConnectionPtr conn;
                Address host;
                std::vector<std::string> methods;
                std::mutex mtx;
                Provider(const BaseConnection::BaseConnectionPtr &c, const Address &h) : conn(c), host(h)
                {
                }
                void addMethod(const std::string &method)
                {
                    std::unique_lock<std::mutex> lock(mtx);
                    methods.push_back(method);
                }
            };

        public:
            // 服务注册的时候调用：将此用户提供的服务维护起来
            void addProvider(const BaseConnection::BaseConnectionPtr &c, const std::string &method, const Address &host)
            {
                // 在_connsMap中查找服务提供者是否存在，存在获取，不存在创建
                Provider::ProviderPtr provider;
                {
                    std::lock_guard<std::mutex> lock(_mutex);
                    auto it = _connsMap.find(c);
                    if (it == _connsMap.end())
                    {
                        // 新增服务提供者
                        provider = std::make_shared<Provider>(c, host);
                        _connsMap[c] = provider;
                    }
                    else
                    {
                        // 获取服务提供者
                        provider = it->second;
                    }

                    // 将提供者插入到_providersMap中
                    auto &providers = _providersMap[method];
                    providers.insert(provider); // 因为set内元素唯一，所有不用担心会重复
                }
                provider->addMethod(method);
            }

            // 连接断开的时候调用
            Provider::ProviderPtr getProvider(const BaseConnection::BaseConnectionPtr &c)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _connsMap.find(c);
                if (it == _connsMap.end())
                    return Provider::ProviderPtr();
                return it->second;
            }

            // 服务提供者连接断开的时候调用(服务提供者不在提供服务了)：删除所有有关服务提供者的信息
            void delProvider(const BaseConnection::BaseConnectionPtr &c)
            {
                // 先在_connsMap中找到服务提供者
                std::lock_guard<std::mutex> lock(_mutex);
                auto it = _connsMap.find(c);
                if (it == _connsMap.end())
                    return;

                // 找到服务提供者，删除其提供过的方法下的信息，即_providersMap中记录
                for (auto &method : it->second->methods)
                {
                    auto &providers = _providersMap[method];
                    providers.erase(it->second);
                }

                // 从_connsMap中删除服务提供者
                _connsMap.erase(it);
            }

            // 服务发现的时候调用：获取方法对应的所有提供者
            std::vector<Address> methodHosts(const std::string &method)
            {
                std::vector<Address> ret;
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _providersMap.find(method);
                if (it == _providersMap.end())
                    return ret;
                for (auto &provider : it->second)
                    ret.push_back(provider->host);
                return ret;
            }

        private:
            // 注意：下面映射表中的provider指向同一个对象
            std::unordered_map<std::string, std::set<Provider::ProviderPtr>> _providersMap;         // <方法名，提供者集合>
            std::unordered_map<BaseConnection::BaseConnectionPtr, Provider::ProviderPtr> _connsMap; // <连接，提供者>
            std::mutex _mutex;
        };

        // 发现者管理类
        class DiscovererManager
        {
        public:
            using DiscovererManagerPtr = std::shared_ptr<DiscovererManager>;

        public:
            // 发现者
            struct Discoverer
            {
                using DiscovererPtr = std::shared_ptr<Discoverer>;
                BaseConnection::BaseConnectionPtr conn;
                std::vector<std::string> methods;
                std::mutex mtx;
                Discoverer(const BaseConnection::BaseConnectionPtr &c) : conn(c)
                {
                }
                void addMethod(const std::string &method)
                {
                    std::unique_lock<std::mutex> lock(mtx);
                    methods.push_back(method);
                }
            };

        public:
            // 服务发现的时候调用：将此用户以及用户发现的服务维护起来
            void addDiscoverer(const BaseConnection::BaseConnectionPtr &c, const std::string &method)
            {
                // 在_connsMap中查找是否有发现者，没有则创建，有则获取
                Discoverer::DiscovererPtr discoverer;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _connsMap.find(c);
                    if (it == _connsMap.end())
                    {
                        // 新建发现者
                        discoverer = std::make_shared<Discoverer>(c);
                        _connsMap[c] = discoverer;
                    }
                    else
                    {
                        discoverer = it->second;
                    }

                    // 将发现者添加到_discoversMap中
                    auto &discoverers = _discoverersMap[method];
                    discoverers.insert(discoverer);
                }
                discoverer->addMethod(method);
            }

            // 发现者断开连接的时候调用(发现者不再发现任何服务)：删除与发现者有关的所有信息
            void delDiscoverer(const BaseConnection::BaseConnectionPtr &c)
            {
                // 先在_connsMap中查找发现者
                auto it = _connsMap.find(c);
                if (it == _connsMap.end())
                    return;

                // 找到发现者，删除其发现过的方法下的信息，即_discoversMap中记录
                for (auto &method : it->second->methods)
                {
                    auto &discoverers = _discoverersMap[method];
                    discoverers.erase(it->second);
                }

                // 从_connsMap中删除
                _connsMap.erase(it);
            }

            // 服务上线通知
            void onlineNotify(const std::string &method, const Address &host)
            {
                serviceNotifyHandler(method, host, ServiceOpType::SERVICE_ONLINE);
            }

            // 服务下线通知
            void offlineNotify(const std::string &method, const Address &host)
            {
                serviceNotifyHandler(method, host, ServiceOpType::SERVICE_OFFLINE);
            }

        private:
            // 服务上下线通知handler
            void serviceNotifyHandler(const std::string &method, const Address &host, ServiceOpType optype)
            {
                // 查找发现过该服务的所有发现者
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _discoverersMap.find(method);
                if (it == _discoverersMap.end())
                    return;

                // 找到 & 构建响应 & 通知
                ServiceRequest::ServiceRequestPtr req = MessageFactory::create<ServiceRequest>();
                req->setId(UUID::uuid());
                req->setMType(MType::REQ_SERVICE);
                req->setOpType(optype);
                req->setMethod(method);
                req->setHost(host);
                for (auto &discoverer : it->second)
                    discoverer->conn->send(req);
            }

        private:
            std::unordered_map<std::string, std::set<Discoverer::DiscovererPtr>> _discoverersMap;
            std::unordered_map<BaseConnection::BaseConnectionPtr, Discoverer::DiscovererPtr> _connsMap;
            std::mutex _mutex;
        };

        // 服务注册中心管理类
        class PDManager
        {
        public:
            using PDManagerPtr = std::shared_ptr<PDManager>;

        public:
            PDManager() : _provider_manager(std::make_shared<ProviderManager>()), _discoverer_manager(std::make_shared<DiscovererManager>())
            {
            }

            // 服务请求回调（被注册给Dispatcher模块，当接收到服务注册 & 服务发现请求时，会调用此函数）
            void onServiceRequest(const BaseConnection::BaseConnectionPtr &conn, ServiceRequest::ServiceRequestPtr &msg)
            {
                if (!msg->check())
                {
                    LOG_ERROR("service request failed, invalid message\n");
                    return;
                }
                ServiceOpType optype = msg->opType();
                if (optype == ServiceOpType::SERVICE_REGISTRY)
                {
                    // 服务注册：维护服务 & 上线通知 & 给服务提供者响应
                    LOG_INFO("service registry:%s:%d -> %s\n", msg->host().first.c_str(), msg->host().second, msg->method().c_str());

                    // 1、维护服务
                    _provider_manager->addProvider(conn, msg->method(), msg->host());

                    // 2、上线通知
                    _discoverer_manager->onlineNotify(msg->method(), msg->host());

                    // 3、响应（给服务提供者的响应）
                    responseProviderHandler(conn, msg);
                }
                else if (optype == ServiceOpType::SERVICE_DISCOVERY)
                {
                    // 服务发现：维护发现者 & 给服务发现者响应
                    LOG_INFO("service discovery: %s\n", msg->method().c_str());

                    // 1、维护服务发现者
                    _discoverer_manager->addDiscoverer(conn, msg->method());

                    // 2、响应（给发现者的响应）
                    responseDiscoverHandler(conn, msg);
                }
                else
                {
                    // 服务未知
                    LOG_INFO("unkown service operation type\n");
                    responseErrorHandler(conn, msg);
                }
            }

            // 连接断开回调
            void onConnShutdown(const BaseConnection::BaseConnectionPtr &conn)
            {
                ProviderManager::Provider::ProviderPtr provider = _provider_manager->getProvider(conn);
                if (provider.get() == nullptr)
                {
                    LOG_INFO("discover is down\n");
                    _discoverer_manager->delDiscoverer(conn);
                    return;
                }
                for (auto &method : provider->methods)
                {
                    LOG_INFO("provider is down\n");
                    _discoverer_manager->offlineNotify(method, provider->host);
                }
                _provider_manager->delProvider(conn);
            }

        private:
            void responseProviderHandler(const BaseConnection::BaseConnectionPtr &conn, const ServiceRequest::ServiceRequestPtr &msg)
            {
                ServiceResponse::ServiceResponsePtr rsp = MessageFactory::create<ServiceResponse>();
                rsp->setId(msg->id());
                rsp->setMType(MType::RSP_SERVICE);
                rsp->setRCode(RCode::RCODE_OK);
                rsp->setOptype(ServiceOpType::SERVICE_REGISTRY); // check函数检查需要依赖操作类型，决定是否对hosts检查
                conn->send(rsp);
            }

            void responseDiscoverHandler(const BaseConnection::BaseConnectionPtr &conn, const ServiceRequest::ServiceRequestPtr &msg)
            {
                ServiceResponse::ServiceResponsePtr rsp = MessageFactory::create<ServiceResponse>();
                rsp->setId(msg->id());
                rsp->setMType(MType::RSP_SERVICE);
                rsp->setOptype(ServiceOpType::SERVICE_DISCOVERY);
                std::vector<Address> hosts = _provider_manager->methodHosts(msg->method());
                if (hosts.empty())
                {
                    rsp->setRCode(RCode::RCODE_NOT_FOUND_SERVICE);
                }
                else
                {
                    rsp->setHosts(hosts);
                    rsp->setMethod(msg->method()); // 当发现者接收到上下线通知的时候，需要将能够提供服务的节点信息缓存起来，而这一处理过程是在回调函数中处理的，需要依赖msg中的method
                    rsp->setRCode(RCode::RCODE_OK);
                }
                conn->send(rsp);
            }

            void responseErrorHandler(const BaseConnection::BaseConnectionPtr &conn, const ServiceRequest::ServiceRequestPtr &msg)
            {
                ServiceResponse::ServiceResponsePtr rsp = MessageFactory::create<ServiceResponse>();
                rsp->setId(msg->id());
                rsp->setMType(MType::RSP_SERVICE);
                rsp->setRCode(RCode::RCODE_INVALID_OPTYPE);
                rsp->setOptype(ServiceOpType::SERVICE_UNKNOWN);
                conn->send(rsp);
            }

        private:
            ProviderManager::ProviderManagerPtr _provider_manager;
            DiscovererManager::DiscovererManagerPtr _discoverer_manager;
        };
    }
}